﻿
//
// 版权所有 (c) 2021-2023 JR
//
// 特此免费授予获得本软件的任何人以处理本软件的权利，但须遵守以下条件：在所有副本或重要部分的软件中必须包括上述版权声明和本许可声明。
//
// 软件按“原样”提供，不提供任何形式的明示或暗示的保证，包括但不限于对适销性、适用性和非侵权的保证。
// 在任何情况下，作者或版权持有人均不对任何索赔、损害或其他责任负责，无论是因合同、侵权或其他方式引起的，与软件或其使用或其他交易有关。

using Masuit.Tools;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Minio.DataModel;
using Nest;
using NewLife;
using OfficeOpenXml.FormulaParsing.Excel.Functions.DateTime;
using QRCoder;
using SqlSugar;
using SqlSugar.Extensions;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.Net;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using static SKIT.FlurlHttpClient.Wechat.Api.Models.ComponentTCBBatchCreateContainerServiceVersionRequest.Types;
using Image = System.Drawing.Image;


namespace EMC.VISITOR.Core;

/// <summary>
/// 通用工具类
/// </summary>
public static class CommonUtil
{
    /// <summary>
    /// 生成百分数
    /// </summary>
    /// <param name="PassCount"></param>
    /// <param name="allCount"></param>
    /// <returns></returns>
    public static string ExecPercent(decimal PassCount, decimal allCount)
    {
        string res = "";
        if (allCount > 0)
        {
            var value = (double)Math.Round(PassCount / allCount * 100, 1);
            if (value < 0)
                res = Math.Round(value + 5 / Math.Pow(10, 0 + 1), 0, MidpointRounding.AwayFromZero).ToString();
            else
                res = Math.Round(value, 0, MidpointRounding.AwayFromZero).ToString();
        }
        if (res == "") res = "0";
        return res + "%";
    }

    /// <summary>
    /// 获取服务地址
    /// </summary>
    /// <returns></returns>
    public static string GetLocalhost()
    {
        return $"{App.HttpContext.Request.Scheme}://{App.HttpContext.Request.Host.Value}";
    }

    /// <summary>
    /// 对象序列化XML
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="obj"></param>
    /// <returns></returns>
    public static string SerializeObjectToXml<T>(T obj)
    {
        if (obj == null) return string.Empty;

        var xs = new XmlSerializer(obj.GetType());
        var stream = new MemoryStream();
        var setting = new XmlWriterSettings
        {
            Encoding = new UTF8Encoding(false), // 不包含BOM
            Indent = true // 设置格式化缩进
        };
        using (var writer = XmlWriter.Create(stream, setting))
        {
            var ns = new XmlSerializerNamespaces();
            ns.Add("", ""); // 去除默认命名空间
            xs.Serialize(writer, obj, ns);
        }
        return Encoding.UTF8.GetString(stream.ToArray());
    }

    /// <summary>
    /// 字符串转XML格式
    /// </summary>
    /// <param name="xmlStr"></param>
    /// <returns></returns>
    public static XElement SerializeStringToXml(string xmlStr)
    {
        try
        {
            return XElement.Parse(xmlStr);
        }
        catch
        {
            return null;
        }
    }

    /// <summary>
    /// 导出模板Excel
    /// </summary>
    /// <param name="fileName"></param>
    /// <param name="fileDto"></param>
    /// <returns></returns>
    public static async Task<IActionResult> ExportExcelTemplate(string fileName, dynamic fileDto)
    {
        fileName = $"{fileName}_{DateTime.Now.ToString("yyyyMMddHHmmss")}.xlsx";

        IImporter importer = new ExcelImporter();
        MethodInfo generateTemplateMethod = importer.GetType().GetMethod("GenerateTemplate");
        MethodInfo closedGenerateTemplateMethod = generateTemplateMethod.MakeGenericMethod(fileDto.GetType());
        var res = await (Task<dynamic>)closedGenerateTemplateMethod.Invoke(importer, new object[] { Path.Combine(App.WebHostEnvironment.WebRootPath, fileName) });

        return new FileStreamResult(new FileStream(res.FileName, FileMode.Open), "application/octet-stream") { FileDownloadName = fileName };
    }

    /// <summary>
    /// 导入数据Excel
    /// </summary>
    /// <param name="file"></param>
    /// <param name="dataDto"></param>
    /// <returns></returns>
    public static async Task<dynamic> ImportExcelData([Required] IFormFile file, dynamic dataDto)
    {
        var newFile = await App.GetRequiredService<SysFileService>().UploadFile(file, "");
        var filePath = Path.Combine(App.WebHostEnvironment.WebRootPath, newFile.FilePath, newFile.Name);

        IImporter importer = new ExcelImporter();
        MethodInfo importMethod = importer.GetType().GetMethod("Import");
        MethodInfo closedImportMethod = importMethod.MakeGenericMethod(dataDto.GetType());
        var res = await (Task<dynamic>)closedImportMethod.Invoke(importer, new object[] { filePath });
        if (res == null || res.Exception != null)
            throw Oops.Oh("导入异常:" + res.Exception);

        return res.Data;
    }
    /// <summary>
    /// 生成小数随机数
    /// </summary>
    /// <param name="ran"></param>
    /// <param name="minValue"></param>
    /// <param name="maxValue"></param>
    /// <returns></returns>
    public static double NextDouble(Random ran, double minValue, double maxValue)
    {
        return ran.NextDouble() * (maxValue - minValue) + minValue;
    }
    /// <summary>
    /// 生成保留指定小数位数（例如 2 位）的随机数
    /// </summary>
    /// <param name="ran"></param>
    /// <param name="minValue"></param>
    /// <param name="maxValue"></param>
    /// <param name="decimalPlace">小数位数</param>
    /// <returns></returns>
    public static double NextDouble(Random ran, double minValue, double maxValue, int decimalPlace)
    {
        double randNum = ran.NextDouble() * (maxValue - minValue) + minValue;
        return Convert.ToDouble(randNum.ToString("f" + decimalPlace));
    }

    /// <summary>
    /// 生成绝对不重复的指定范围的随机数
    /// </summary>
    /// <param name="minValue"></param>
    /// <param name="maxValue"></param>
    /// <param name="n">生成随机数个数</param>
    /// <returns></returns>
    public static int[] GenerateUniqueRandom(int minValue, int maxValue, int n)
    {
        // Random.Nex(1, 10) 只能产生到 9 的随机数，若要产生到 10 的随机数， maxValue 要加 1
        maxValue++;

        // Random.Nex(1, 10) 只能产生 9 个随机数，因此 n 不能大于 10 - 1
        if (n > maxValue - minValue)
            n = maxValue - minValue;

        int[] arr = new int[n];
        Random ran = new Random((int)DateTime.Now.Ticks);

        bool flag = true;
        for (int i = 0; i < n; i++)
        {
            do
            {
                int val = ran.Next(minValue, maxValue);
                if (!IsDuplicates(ref arr, val))
                {
                    arr[i] = val;
                    flag = false;
                }
            } while (flag);
            if (!flag)
                flag = true;
        }
        return arr;
    }

    // 查检当前生成的随机数是否重复
    static bool IsDuplicates(ref int[] arr, int currRandNum)
    {
        bool flag = false;
        for (int i = 0; i < arr.Length; i++)
        {
            if (arr[i] == currRandNum)
            {
                flag = true;
                break;
            }
        }
        return flag;
    }

    /// <summary>
    /// 获取中文格式日期
    /// </summary>
    /// <param name="dt"></param>
    /// <returns></returns>
    public static string GetZNDate(DateTime dt)
    {
        // 创建一个CultureInfo实例，用于指定中文的日期和时间格式
        CultureInfo culture = new CultureInfo("zh-CN");
        // 创建一个DateTimeFormatInfo实例，用于指定中文的日期格式
        DateTimeFormatInfo dtfi = culture.DateTimeFormat;
        dtfi.ShortDatePattern = "yyyy年MM月dd日";
        // 将DateTime对象转换为中文格式的字符串
        string dateStr = dt.ToString("d", dtfi);
        return dateStr;
    }
    /// <summary>
    /// 获取本周所有的日期
    /// </summary>
    /// <returns></returns>
    public static List<DateTime> GetCurrentWeekDates()
    {
        List<DateTime> currentWeekDates = new List<DateTime>();
        var startDate = GetMondayDate();
        var endDate = startDate.AddDays(7);
        for (DateTime i = startDate; i < endDate; i = i.AddDays(1))
        {
            currentWeekDates.Add(i);
        }
        return currentWeekDates;
    }
    /// <summary>
    /// 本周周一日期，返回结果格式：2014-5-5 0:00:00
    /// </summary>
    /// <returns></returns>
    public static DateTime GetMondayDate()
    {
        DateTime dt = DateTime.Now;
        int today = (int)dt.DayOfWeek;
        if (dt.DayOfWeek.ToString() != "Sunday")//也可以使用today!=0
        {
            return dt.AddDays(1 - today).Date;
        }
        else
        {
            return dt.AddDays(-6 - today).Date;//若今天是周日，获取到的周一日期是下周一的日期，所以要减去7天
        }
    }

    //本周日日期

    public static DateTime GetSundayDate()
    {
        DateTime dt = DateTime.Now;
        int today = (int)dt.DayOfWeek;
        if (dt.DayOfWeek.ToString() != "Sunday")//也可以使用today!=0
        {
            return dt.AddDays(7 - today).Date;
        }
        else
        {
            return dt.AddDays(-today).Date;//若今天是周日，获取到的周日日期是下周日的日期，所以要减去7天
        }
    }



    /// <summary>
    /// 上周一日期
    /// </summary>
    /// <returns></returns>
    public static DateTime GetLastMondayDate()
    {
        DateTime dt = DateTime.Now;
        int today = (int)dt.DayOfWeek;
        if (dt.DayOfWeek.ToString() != "Sunday")//也可以使用today!=0
        {
            return dt.AddDays(-today - 6).Date;
        }
        else
        {
            return dt.AddDays(-today - 13).Date;//若今天是周日，获取到的上周一的日期是本周周一的日期，所以要减去7天
        }
    }



    /// <summary>
    /// 上周日日期
    /// </summary>
    /// <returns></returns>
    public static DateTime GetLastSundayDate()
    {
        DateTime dt = DateTime.Now;

        int today = (int)dt.DayOfWeek;

        if (dt.DayOfWeek.ToString() != "Sunday")//也可以使用today!=0
        {
            return dt.AddDays(-today).Date;
        }
        else
        {
            return dt.AddDays(-today - 7).Date;//若今天是周日，获取到的上周日的日期是本周周日的日期，所以要减去7天
        }
    }

    /// <summary>
    /// 下周周一日期
    /// </summary>
    /// <returns></returns>
    public static DateTime GetNextMondayDate()
    {
        DateTime dt = DateTime.Now;
        int today = (int)dt.DayOfWeek;

        if (dt.DayOfWeek.ToString() != "Sunday")//也可以使用today!=0
        {
            return dt.AddDays(-today + 8).Date;
        }
        else
        {
            return dt.AddDays(-today + 1).Date;//若今天是周日，获取到的下周周一日期是下下周一的日期，所以要减去7天
        }
    }

    /// <summary>
    /// 下周周日日期
    /// </summary>
    /// <returns></returns>
    public static DateTime GetNextSundayDate()
    {
        DateTime dt = DateTime.Now;
        int today = (int)dt.DayOfWeek;
        if (dt.DayOfWeek.ToString() != "Sunday")//也可以使用today!=0
        {
            return dt.AddDays(14 - today).Date;
        }
        else
        {
            return dt.AddDays(7 - today).Date;//若今天是周日，获取到的下周周日日期是下下周日的日期，所以要减去7天
        }
    }
    /// <summary>
    /// 获取服务端地址
    /// </summary>
    /// <returns></returns>
    public static string GetServerIP()
    {
        var context = App.HttpContext;
        string serverAddress = context.Request.Host.Value; // 获取完整的主机名（包括域名或 IP 地址）
        string scheme = context.Request.Scheme; // 获取 HTTP/HTTPS 等协议类型
        // 构建完整的服务器地址
        var uriBuilder = new UriBuilder(scheme, serverAddress);
        string fullServerAddress = uriBuilder.ToString();
        if (fullServerAddress.Contains("localhost"))
        {
            var IP = System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName())
              .AddressList.FirstOrDefault(address => address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)?.ToString();
            return $"http://{IP}:{context.Request.Host.Port}/";
        }
        else
            return fullServerAddress.Replace("[", null).Replace("]", null);
    }
    /// <summary>
    /// 获取类中的属性名称和值
    /// </summary>
    /// <returns>所有属性名称</returns>
    public static List<string> GetProperties<T>(T t)
    {
        List<string> ListStr = new List<string>();
        if (t == null)
        {
            return ListStr;
        }
        System.Reflection.PropertyInfo[] properties = t.GetType().GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
        if (properties.Length <= 0)
        {
            return ListStr;
        }
        foreach (System.Reflection.PropertyInfo item in properties)
        {
            string name = item.Name; //名称
            object value = item.GetValue(t, null);  //值

            if (item.PropertyType.IsValueType || item.PropertyType.Name.StartsWith("String"))
            {
                ListStr.Add(name);
            }
            else
            {
                GetProperties(value);
            }
        }
        return ListStr;
    }

    /// <summary>
    ///  获取类中的字段的名称和值
    /// </summary>
    /// <returns>所有字段名称</returns>
    public static List<string> GetFields<T>(T t)
    {

        List<string> ListStr = new List<string>();
        if (t == null)
        {
            return ListStr;
        }
        System.Reflection.FieldInfo[] fields = t.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
        if (fields.Length <= 0)
        {
            return ListStr;
        }
        foreach (System.Reflection.FieldInfo item in fields)
        {
            string name = item.Name; //名称
            object value = item.GetValue(t);  //值

            if (item.FieldType.IsValueType || item.FieldType.Name.StartsWith("String"))
            {
                ListStr.Add(name);
            }
            else
            {
                GetFields(value);
            }
        }
        return ListStr;


    }


    /// <summary>
    /// 获取类中的属性名称和值
    /// </summary>
    /// <returns>所有属性名称</returns>
    public static List<string> GetPropertiesName<T>()
    {
        var t = Activator.CreateInstance<T>();
        List<string> ListStr = new List<string>();
        if (t == null)
        {
            return ListStr;
        }
        System.Reflection.PropertyInfo[] properties = t.GetType().GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
        if (properties.Length <= 0)
        {
            return ListStr;
        }
        foreach (System.Reflection.PropertyInfo item in properties)
        {
            string name = item.Name; //名称

            var mapper = item.GetCustomAttribute<NotMapperErpField>();
            if (mapper != null)
                continue;


            var jsonP = item.GetCustomAttribute<JsonPropertyAttribute>();
            if (jsonP != null)
            {
                if (!string.IsNullOrWhiteSpace(jsonP.PropertyName))
                {
                    ListStr.Add(jsonP.PropertyName);
                    continue;
                }
            }

            var sugarC = item.GetCustomAttribute<SugarColumn>();
            if (sugarC != null && sugarC.IsIgnore)
                continue;




            ListStr.Add(name);
        }
        return ListStr;
    }

    /// <summary>
    ///  获取类中的字段的名称和值
    /// </summary>
    /// <returns>所有字段名称</returns>
    public static List<string> GetFieldsName<T>()
    {
        var t = Activator.CreateInstance<T>();
        List<string> ListStr = new List<string>();
        if (t == null)
        {
            return ListStr;
        }
        System.Reflection.FieldInfo[] fields = t.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
        if (fields.Length <= 0)
        {
            return ListStr;
        }
        foreach (System.Reflection.FieldInfo item in fields)
        {
            string name = item.Name; //名称
            var mapper = item.GetCustomAttribute<NotMapperErpField>();
            if (mapper != null)
                continue;


            var jsonP = item.GetCustomAttribute<JsonPropertyAttribute>();
            if (jsonP != null)
            {
                if (!string.IsNullOrWhiteSpace(jsonP.PropertyName))
                {
                    ListStr.Add(jsonP.PropertyName);
                    continue;
                }
            }

            var sugarC = item.GetCustomAttribute<SugarColumn>();
            if (sugarC != null && sugarC.IsIgnore)
                continue;



            ListStr.Add(name);
        }
        return ListStr;


    }

    /// <summary>                 
    /// 将对象属性转换为key-value对        
    /// </summary>        
    /// <param name="o"></param>        
    /// <returns></returns>        
    public static Dictionary<String, object> ToMap(Object o)
    {
        Dictionary<String, Object> map = new Dictionary<string, object>();
        Type t = o.GetType();
        PropertyInfo[] pi = t.GetProperties(BindingFlags.Public | BindingFlags.Instance);
        foreach (PropertyInfo p in pi)
        {

            //如果该属性是索引 跳过本次循环
            if (p.GetIndexParameters().Length > 0)
            {
                continue;
            }

            MethodInfo mi = p.GetGetMethod();
            if (mi != null && mi.IsPublic)
            {
                map.Add(p.Name, mi.Invoke(o, new Object[] { }));
            }
        }
        return map;
    }


    #region 带logo的二维码
    /// <summary>
    /// 
    /// </summary>
    /// <param name="url">存储内容</param>
    /// <param name="pixel">像素大小</param>
    /// <returns></returns>
    public static Bitmap GetLogoQRCode(string url, string logoPath, int pixel)
    {
        QRCodeGenerator generator = new QRCodeGenerator();
        QRCodeData codeData = generator.CreateQrCode(url, QRCodeGenerator.ECCLevel.M, true);
        QRCoder.QRCode qrcode = new QRCoder.QRCode(codeData);
        Bitmap icon = new Bitmap(logoPath);
        Bitmap qrImage = qrcode.GetGraphic(pixel, Color.Black, Color.White, icon, 15, 6, true);
        #region 参数介绍
        //GetGraphic方法参数介绍
        //pixelsPerModule //生成二维码图片的像素大小 ，我这里设置的是5
        //darkColor       //暗色   一般设置为Color.Black 黑色
        //lightColor      //亮色   一般设置为Color.White  白色
        //icon             //二维码 水印图标 例如：Bitmap icon = new Bitmap(context.Server.MapPath("~/images/zs.png")); 默认为NULL ，加上这个二维码中间会显示一个图标
        //iconSizePercent  //水印图标的大小比例 ，可根据自己的喜好设置
        //iconBorderWidth  // 水印图标的边框
        //drawQuietZones   //静止区，位于二维码某一边的空白边界,用来阻止读者获取与正在浏览的二维码无关的信息 即是否绘画二维码的空白边框区域 默认为true
        #endregion
        return qrImage;
    }
    #endregion

    /// <summary>
    /// 生成二维码图片 (普通二维码)
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    private static Bitmap GetPTQRCode(string str, int pixel = 5)
    {
        QRCodeGenerator qrGenerator = new QRCodeGenerator();
        QRCodeData qrCodeData = qrGenerator.CreateQrCode(str, QRCodeGenerator.ECCLevel.Q, true);
        QRCoder.QRCode qrCode = new QRCoder.QRCode(qrCodeData);
        Bitmap bt = qrCode.GetGraphic(pixel, Color.Black, Color.White, true);

        return bt;
    }

    /// <summary>
    /// 生成二维码图片流（二维码上含文字）
    /// </summary>
    /// <param name="url">二维码中要传递的数据（地址）</param>
    /// <param name="title">二维码上显示的文字说明</param>
    public static byte[] GenerateQrCode(string url, string title)
    {
        using (System.Drawing.Image codeImage = GetPTQRCode(url), strImage = ConvertStringToImage(title))
        {
            System.Drawing.Image img = CombineImage(600, 600, codeImage, 0, 0, strImage, 0, 550);
            using (var stream = new MemoryStream())
            {
                img.Save(stream, ImageFormat.Jpeg);
                //输出图片流
                return stream.ToArray();
            }
        }
    }

    /// <summary>
    /// 生成二维码图片流（二维码上含文字）
    /// </summary>
    /// <param name="str1">二维码中要传递的数据（地址）</param>
    /// <param name="str2">二维码上显示的文字说明</param>
    public static System.Drawing.Image GenerateQrCodeImg(string str1, string str2)
    {
        System.Drawing.Image img;
        using (System.Drawing.Image codeImage = GetPTQRCode(str1), strImage = ConvertStringToImage(str2))
        {
            img = CombineImage(600, 600, codeImage, 0, 0, strImage, 0, 550);
        }
        return img;
    }

    /// <summary>
    /// 生成文字图片
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    private static System.Drawing.Image ConvertStringToImage(string str)
    {
        Bitmap image = new Bitmap(600, 40, PixelFormat.Format24bppRgb);

        Graphics g = Graphics.FromImage(image);

        try
        {
            Font font = new Font("SimHei", 14, FontStyle.Regular);

            g.Clear(Color.White);

            StringFormat format = new StringFormat();
            format.Alignment = StringAlignment.Center;
            format.LineAlignment = StringAlignment.Center;

            Rectangle rectangle = new Rectangle(0, 0, 600, 40);

            g.DrawString(str, font, new SolidBrush(Color.Black), rectangle, format);

            return image;
        }
        catch (Exception e)
        {
            throw e;
        }
        finally
        {
            GC.Collect();
        }
    }


    /// <summary>
    /// 在画板中合并二维码图片和文字图片
    /// </summary>
    /// <param name="width"></param>
    /// <param name="height"></param>
    /// <param name="imgLeft"></param>
    /// <param name="imgLeft_left"></param>
    /// <param name="imgLeft_top"></param>
    /// <param name="imgRight"></param>
    /// <param name="imgRight_left"></param>
    /// <param name="imgRight_top"></param>
    /// <returns></returns>
    private static System.Drawing.Image CombineImage(int width, int height, System.Drawing.Image imgLeft, int imgLeft_left, int imgLeft_top, System.Drawing.Image imgRight, int imgRight_left, int imgRight_top)
    {
        Bitmap image = new Bitmap(width, height, PixelFormat.Format24bppRgb);

        Graphics g = Graphics.FromImage(image);

        try
        {
            g.Clear(Color.White);
            g.DrawImage(imgLeft, imgLeft_left, imgLeft_top, 600, 600);
            g.DrawImage(imgRight, imgRight_left, imgRight_top, imgRight.Width, imgRight.Height);

            return image;
        }
        catch (Exception e)
        {
            throw e;
        }
        finally
        {
            g.Dispose();
        }
    }

    #region 去除HTML标记(用正则彻底去除HTML\CSS\script代码 )
    /// <summary>
    /// 去除HTML标记(用正则彻底去除HTML\CSS\script代码 )
    /// </summary>
    /// <param name="Htmlstring">包括HTML的源码 </param>
    /// <returns>已经去除后的文字</returns>
    public static string NoHTML(this string Htmlstring)
    {
        //删除脚本
        Htmlstring = Regex.Replace(Htmlstring, @"<script[^>]*?>.*?</script>", "", RegexOptions.IgnoreCase);
        //删除HTML
        Htmlstring = Regex.Replace(Htmlstring, @"<(.[^>]*)>", "", RegexOptions.IgnoreCase);
        Htmlstring = Regex.Replace(Htmlstring, @"([\r\n])[\s]+", "", RegexOptions.IgnoreCase);
        Htmlstring = Regex.Replace(Htmlstring, @"-->", "", RegexOptions.IgnoreCase);
        Htmlstring = Regex.Replace(Htmlstring, @"<!--.*", "", RegexOptions.IgnoreCase);
        Htmlstring = Regex.Replace(Htmlstring, @"&(quot|#34);", "\"", RegexOptions.IgnoreCase);
        Htmlstring = Regex.Replace(Htmlstring, @"&(amp|#38);", "&", RegexOptions.IgnoreCase);
        Htmlstring = Regex.Replace(Htmlstring, @"&(lt|#60);", "<", RegexOptions.IgnoreCase);
        Htmlstring = Regex.Replace(Htmlstring, @"&(gt|#62);", ">", RegexOptions.IgnoreCase);
        Htmlstring = Regex.Replace(Htmlstring, @"&(nbsp|#160);", " ", RegexOptions.IgnoreCase);
        Htmlstring = Regex.Replace(Htmlstring, @"&(iexcl|#161);", "\xa1", RegexOptions.IgnoreCase);
        Htmlstring = Regex.Replace(Htmlstring, @"&(cent|#162);", "\xa2", RegexOptions.IgnoreCase);
        Htmlstring = Regex.Replace(Htmlstring, @"&(pound|#163);", "\xa3", RegexOptions.IgnoreCase);
        Htmlstring = Regex.Replace(Htmlstring, @"&(copy|#169);", "\xa9", RegexOptions.IgnoreCase);
        Htmlstring = Regex.Replace(Htmlstring, @"&#(\d+);", "", RegexOptions.IgnoreCase);

        Htmlstring.Replace("<", "");
        Htmlstring.Replace(">", "");
        Htmlstring.Replace("\r\n", "");
        Htmlstring = HttpUtility.HtmlEncode(Htmlstring).Trim();
        return Htmlstring;
    }
    #endregion

    #region 生成系统流水号
    /// <summary>
    /// 生成系统流水号
    /// </summary>
    /// <param name="type">流水号类型</param>
    /// <param name="_rep">数据库上下文</param>
    /// <returns></returns>
    public static async Task<string> GenerationSerialnumber(OrderType type, ISqlSugarClient _rep)
    {
        var rep = App.GetRequiredService<SqlSugarRepository<BD_CodeGeneration>>();
        var codeGeneration = await rep.GetFirstAsync(r => r.OrderType == type);
        if (codeGeneration == null) { return default(string); }

        var serialNumberModel = await GenerationSerialnumberModel(codeGeneration, type, rep.Context);
        if (serialNumberModel == null) { return default(string); }

        string month = serialNumberModel.month.ToString();
        string day = serialNumberModel.day.ToString();
        if (month != "0" && month.Length == 1)
            month = $"0{month}";

        if (day != "0" && day.Length == 1)
            day = $"0{day}";

        string[] serialInfos = new string[]
        {
            codeGeneration.Prefix,
            serialNumberModel.year.ToString(),
            month,
            day,
           NumberConvent(codeGeneration.Serialnumber, serialNumberModel.number)
        };
        return string.Join(CommonConst.SysDefaultSerialNumberJoinString, serialInfos.Where(r => !r.IsNullOrEmpty() && r != "0").ToList());
    }
    /// <summary>
    /// 根据规则给未达到要求的流水号补0
    /// </summary>
    /// <param name="serNumberRuleLength">流水号位数规则</param>
    /// <param name="realNumber">实际流水号</param>
    /// <returns></returns>
    private static string NumberConvent(int serNumberRuleLength, int realNumber)
    {
        if (serNumberRuleLength <= 0 || serNumberRuleLength > CommonConst.SysDefaultSerialNumberLenth)
            serNumberRuleLength = CommonConst.SysDefaultSerialNumberLenth;

        if (realNumber.ToString().Length >= serNumberRuleLength)
            return realNumber.ToString();

        string str = string.Empty;
        for (int i = 0; i < serNumberRuleLength - realNumber.ToString().Length; i++)
        {
            str += "0";
        }
        return str + realNumber;
    }

    private static async Task<SysSerialNumber> GenerationSerialnumberModel(BD_CodeGeneration codeGeneration, OrderType type, ISqlSugarClient _rep)
    {
        SysSerialNumber sysSerialNumber = null;
        DateTime time = DateTime.Today;
        string yearMTemp = time.Year.ToString();
        //去掉
        int yearM = Convert.ToInt32(yearMTemp.Substring(2));
        int monthM = time.Month;
        int dayM = time.Day;
        switch (codeGeneration.DateType)
        {
            case CodeGenerationDateType.年:
                monthM = 0;
                dayM = 0;
                break;
            case CodeGenerationDateType.年月:
                dayM = 0;
                break;
            case CodeGenerationDateType.年月日:
                break;
            case CodeGenerationDateType.年月日时分秒:
                break;
        }
        var name = new SugarParameter("@name", type.GetDescription());
        var year = new SugarParameter("@year", yearM);
        var month = new SugarParameter("@month", monthM);
        var day = new SugarParameter("@day", dayM);

        var dbType = _rep.CurrentConnectionConfig.DbType;
        switch (dbType)
        {
            case SqlSugar.DbType.SqlServer:
                sysSerialNumber = SqlServerSerialNumber(name, year, month, day, _rep);
                break;
            case SqlSugar.DbType.MySql:
                sysSerialNumber = MysqlSerialNumber(name, year, month, day, _rep);
                break;
            case SqlSugar.DbType.PostgreSQL:
                sysSerialNumber = PgsqlSerialNumber(name, year, month, day, _rep);
                break;
            default:
                sysSerialNumber = OtherSerialNumber(name, year, month, day, _rep);
                break;
        }
        return sysSerialNumber;
    }
    private static SysSerialNumber SqlServerSerialNumber(SugarParameter name, SugarParameter year, SugarParameter month, SugarParameter day, ISqlSugarClient _rep)
    {
        var numbers = _rep.Ado.UseStoredProcedure().SqlQuery<SysSerialNumber>("nsc_get_next_serial_number", name, year, month, day);
        return numbers.FirstOrDefault();
    }
    private static SysSerialNumber MysqlSerialNumber(SugarParameter name, SugarParameter year, SugarParameter month, SugarParameter day, ISqlSugarClient _rep)
    {
        //var result = _sqlSugarClient.Ado.SqlQuery<string>($"select select_tlsh('{prefix}','{type.ToString()}')");
        var numbers = _rep.Ado.UseStoredProcedure().SqlQuery<SysSerialNumber>("nsc_get_next_serial_number", name, year, month, day);
        return numbers.FirstOrDefault();
    }
    private static SysSerialNumber PgsqlSerialNumber(SugarParameter name, SugarParameter year, SugarParameter month, SugarParameter day, ISqlSugarClient _rep)
    {
        var numbers = _rep.Ado.SqlQuery<string>($"select nsc_get_next_serial_number('{name.Value}','{year.Value}','{month.Value}','{day.Value}')");
        var num = numbers.FirstOrDefault();
        var serialNumber = num.TrimStart(string.Join("", year.Value, month.Value, day.Value));
        return new SysSerialNumber
        {
            name = name.Value.ToString(),
            year = year.Value.ObjToInt(),
            month = month.Value.ObjToInt(),
            day = day.Value.ObjToInt(),
            number = serialNumber.ObjToInt(),
        };
    }
    private static SysSerialNumber OtherSerialNumber(SugarParameter name, SugarParameter year, SugarParameter month, SugarParameter day, ISqlSugarClient _rep)
    {
        var numbers = _rep.Ado.UseStoredProcedure().SqlQuery<SysSerialNumber>("nsc_get_next_serial_number", name, year, month, day);
        return numbers.FirstOrDefault();
    }
    #endregion

    private static async Task<SysConfig> GetConfig(string code)
    {
        var rep = App.GetRequiredService<SqlSugarRepository<SysConfig>>();
        var config = await rep.GetFirstAsync(r => r.Code == code.Trim());
        if (config == null || config.Value.ObjToBool() == false)
            return null;

        return config;
    }
    private static async Task<string> GetConfigRemark(string code)
    {
        var config = await GetConfig(code);
        return config?.Remark;
    }
    public static async Task<string[]> GetConfigRemarkSplits(string code, string splitString)
    {
        var remark = await GetConfigRemark(code);
        if (!remark.IsNullOrEmpty())
            return remark.Split(splitString);

        return new string[] { };
    }
    public static async Task<int> GetConfigRemarkToInt(string code)
    {
        var config = await GetConfig(code);
        if (config == null)
            return 0;
        return config.Remark.ObjToInt();
    }
    public static async Task<Dictionary<string, string>> GetConfigValue(params string[] codes)
    {
         var rep = App.GetRequiredService<SqlSugarRepository<SysConfig>>();
        var configs = await rep.GetListAsync(r => codes.Contains(r.Code));
        return configs.ToDictionary(r => r.Code, r => r.Value);
    }

    public static async Task<string> GetConfigValue(string code)
    {
        var rep = App.GetRequiredService<SqlSugarRepository<SysConfig>>();
        var config = await rep.GetFirstAsync(r => r.Code == code.Trim());
        return config?.Value;
    }
    public static async Task<bool> GetConfigValueToBool(string code)
    {
        var config = await GetConfig(code);
        if (config == null)
            return false;

        var flag = config?.Value.ObjToBool();
        if (!flag.HasValue)
            return false;

        return flag.Value;
    }
    /// <summary>
    /// 判断是否是开发环境
    /// </summary>
    /// <param name="devName">开发环境配置名称</param>
    /// <returns>true:是开发环境  false:不是开发环境</returns>
    private static bool IsConfigurationDev(string devName = "ConfigurationDev")
    {
        var systemEnvs = App.GetConfig<string[]>("ConfigurationScanDirectories");
        if (systemEnvs != null && systemEnvs.Contains(devName))
            return true;

        return false;
    }
    /// <summary>
    /// 是否在开发环境时跳过调用第三方接口的逻辑
    /// </summary>
    /// <param name="devName">开发环境配置名称</param>
    /// <returns>true:跳过  false:不跳过</returns>
    public static async Task<bool> IsOpenDevEnvJumpOtherSystemApi(string devName = "ConfigurationDev")
    {
        if (await GetConfigValueToBool(CommonConst.JumpOtherSystemApi) && IsConfigurationDev(devName))
            return true;

        return false;
    }

    /// <summary>
    /// 获取作业系数
    /// </summary>
    /// <param name="db">数据库上下文</param>
    /// <param name="materialCode">物料号 必填</param>
    /// <param name="wocenterCode">工作中心 必填</param>
    /// <param name="processCode">工序号</param>
    /// <param name="processName">工序名称</param>
    /// <returns></returns>
    public static async Task<decimal> GetInvRepCoeff(ISqlSugarClient db, string materialCode, string wocenterCode, string processCode, string processName)
    {
        string sql = string.Empty;
        var dbType = db.CurrentConnectionConfig.DbType;
        switch (dbType)
        {
            case SqlSugar.DbType.SqlServer:
            case SqlSugar.DbType.MySql:
                sql = $"select repcoeff from bd_processinventory where cinvcode='{materialCode}' and centercode='{wocenterCode.Trim()}' and opnumdesc='{processName.Replace(" ", "").Trim()}' and flag=1 ";
                break;
            case SqlSugar.DbType.PostgreSQL:
                sql = $"select repcoeff from bd_processinventory where cinvcode='{materialCode}' and centercode='{wocenterCode.Trim()}' and opnumdesc='{processName.Replace(" ", "").Trim()}' and flag=true ";
                break;

        }


        var inv = await db.Ado.SqlQueryAsync<dynamic>(sql);

        if (inv.Count > 0)
            return Convert.ToDecimal(inv[0].repcoeff);

        return 1;
    }

    public static async Task<List<SysDictType>> LoadDictTypesByCode(this ISqlSugarClient _db, params string[] dicTypeCodes)
    {
        var dicTypeList = await _db.MyQueryable<SysDictType>().Includes(r => r.SysDictDatas).Where(r => dicTypeCodes.Contains(r.Code) && r.Status == StatusEnum.Enable).ToListAsync();
        dicTypeList.ForEach(dicType =>
        {
            dicType.SysDictDatas = dicType.SysDictDatas.Where(r => r.Status == StatusEnum.Enable).ToList();
        });
        return dicTypeList;
    }
    public static async Task<SysDictType> LoadDictTypesByCode(this ISqlSugarClient _db, string dicTypeCode)
    {
        var dicType = await _db.MyQueryable<SysDictType>().Includes(r => r.SysDictDatas).Where(r => r.Code == dicTypeCode && r.Status == StatusEnum.Enable).FirstAsync();
        dicType.SysDictDatas = dicType.SysDictDatas.Where(r => r.Status == StatusEnum.Enable).ToList();
        return dicType;
    }
    public static string LoadDictDataByCode(this IEnumerable<SysDictType> dicDatas, string dictTypeCode, string dictDataCode)
    {
        return dicDatas.FirstOrDefault(r => r.Code == dictTypeCode)?.SysDictDatas.FirstOrDefault(c => c.Code == dictDataCode)?.Value;
    }
    public static SysDictType LoadDictDataByCode(this List<SysDictType> dicDatas, string dictTypeCode)
    {
        return dicDatas.FirstOrDefault(r => r.Code == dictTypeCode);
    }
    public static string LoadDictDataByCode(this SysDictType dic, string dictDataCode)
    {
        return dic.SysDictDatas.FirstOrDefault(c => c.Code == dictDataCode)?.Value;
    }


    /// <summary>
    /// 讀取文件並壓縮文件
    /// </summary>
    /// <param name="file"></param>
    /// <param name="savePath"></param>
    /// <returns></returns>
    public static byte[] DownImage(IFormFile file, string savePath, long quality = 20)
    {
        var fileStream = file.OpenReadStream();
        var imageBytes = fileStream.ReadBytes(file.Length);
        imageBytes = CompressImage(imageBytes, quality);
        File.WriteAllBytes(savePath, imageBytes);
        return imageBytes;
    }
    /// <summary>
    /// 下載圖片後壓縮圖片
    /// </summary>
    /// <param name="imageHttpUrl">圖片http url 地址</param>
    /// <param name="quality">壓縮值</param>
    /// <returns>壓縮後的二進制byte[]</returns>
    public static byte[] DownImage(string imageHttpUrl, long quality = 20)
    {
        using (WebClient client = new WebClient())
        {
            var imageBytes = client.DownloadData(imageHttpUrl);
            //压缩图片
            imageBytes = CompressImage(imageBytes, quality);
            return imageBytes;
        }
    }


    /// <summary>
    /// 下載圖片後壓縮圖片
    /// </summary>
    /// <param name="imageHttpUrl">圖片http url 地址</param>
    /// <param name="savePath">保存的地址</param>
    /// <param name="quality">壓縮值：0-100</param>
    /// <param name="minByteValue">最小byte值，當文件小於 200KB 則不壓縮圖片 (0.2 * 1024 * 1024)</param>
    /// <returns></returns>
    public static string DownImageToBase64String(string imageHttpUrl, string savePath, long quality = 20, double minByteValue = 0.2 * 1024 * 1024)
    {
        using (WebClient client = new WebClient())
        {
            var imageBytes = client.DownloadData(imageHttpUrl);
            //压缩图片
            if (imageBytes.Length > minByteValue)
            {
                imageBytes = CompressImage(imageBytes, quality);
            }
            File.WriteAllBytes(savePath, imageBytes);
            return Convert.ToBase64String(imageBytes);
        }
    }
    /// <summary>
    /// 壓縮圖片
    /// </summary>
    /// <param name="imageBytes"></param>
    /// <param name="quality"></param>
    /// <returns></returns>
    /// <exception cref="ArgumentOutOfRangeException"></exception>
    /// <exception cref="NotSupportedException"></exception>
    private static byte[] CompressImage(byte[] imageBytes, long quality)
    {
        // Check if the quality is within the valid range
        if (quality < 0L || quality > 100L)
        {
            throw new ArgumentOutOfRangeException(nameof(quality), "Quality must be between 0 and 100.");
        }

        using (var ms = new MemoryStream(imageBytes))
        {
            using (var originalImage = Image.FromStream(ms))
            {
                // Create a new MemoryStream to hold the compressed image
                using (var compressedStream = new MemoryStream())
                {
                    // Get the JPEG encoder
                    var encoder = GetEncoder(ImageFormat.Jpeg);
                    if (encoder == null)
                    {
                        throw new NotSupportedException("JPEG encoder not found.");
                    }

                    // Create an EncoderParameters object with one EncoderParameter object in the array
                    var encoderParameters = new EncoderParameters(1);
                    encoderParameters.Param = new EncoderParameter[] { new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality) };

                    // Save the image to the MemoryStream with the JPEG encoder and the quality parameter
                    originalImage.Save(compressedStream, encoder, encoderParameters);

                    // Return the compressed image as a byte array
                    return compressedStream.ToArray();
                }
            }
        }
    }

    private static ImageCodecInfo GetEncoder(ImageFormat format)
    {
        var codecs = ImageCodecInfo.GetImageDecoders();
        foreach (var codec in codecs)
        {
            if (codec.FormatID == format.Guid)
            {
                return codec;
            }
        }
        return null;
    }

    /// <summary>
    /// 正则匹配IP和端口
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public static List<string> ExtractIPAndPort(string input)
    {
        var ipPortPattern = @"\b(?<IP>[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3})\b:\b(?<Port>\d+)\b";
        var matches = Regex.Matches(input, ipPortPattern);
        var ipPortPairs = new List<string>();

        foreach (Match match in matches)
        {
            ipPortPairs.Add($"{match.Groups["IP"].Value}:{match.Groups["Port"].Value}");
        }

        return ipPortPairs;
    }

    /// <summary>
    /// 正则匹配域名
    /// </summary>
    /// <param name="url"></param>
    /// <returns></returns>
    public static string GetHostFromUrl(string url)
    {
        string p = @"(http|https)://(?<domain>[^(:|/]*)";
        Regex reg = new Regex(p, RegexOptions.IgnoreCase);
        Match m = reg.Match(url);
        var Result = m.Groups["domain"].Value;
        return Result;
    }
}

/// <summary>
/// 默认忽略
/// </summary>
public class NotMapperErpField : Attribute
{

}